// geometric_problem.h
// 04/15/2011: created

#pragma once

#include <string>
#include <QtGui/QWidget>
#include <QtGui/QPainter>
#include <vector>
#include "arma_vector.h"
#include "Log.h"

/** Axis : derived from QPainter
  */
class Axis
{
protected:
  QWidget *m_widget;
  double m_xmin, m_ymin, m_xmax, m_ymax;
public:
  Axis(QWidget* w) : m_widget(w), m_xmin(-10), m_ymin(-10), m_xmax(10), m_ymax(10) {}
  QPointF point(double x, double y);
  QPointF reversePoint(const QPointF& p);
  QPointF point(const vec& v);
  void setBoundary(double xmin, double xmax, double ymin, double ymax) {
    m_xmin = xmin; m_xmax = xmax; m_ymin = ymin, m_ymax = ymax;
  }
  QPointF distance(double x, double y);
};

/** Plotter : derived from QPainter
  */
class Plotter : public QPainter, public Axis
{
public:
  Plotter(QWidget* w) : QPainter(w), Axis(w) {}
};

/** A problem solver template, loadData(), solve()
  */
class Problem 
{
protected:
  std::string m_name;
public:
  Problem(const std::string& name) : m_name(name) {}
  virtual int n_data() = 0;
  virtual void loadData(const std::vector<std::string>& fName, bool binary) = 0;
  virtual void solve() = 0;
  virtual void draw(Plotter& pl) = 0;
  const std::string& name() const { return m_name; }
};

/** An example: finding midpoints of edges of a polygon */
class PolygonMidpoint : public Problem
{
  std::vector<vec> m_vertex;
  std::vector<vec> m_midpoint;
public:
  PolygonMidpoint(const std::string& name = "Find midpoints in polygon") : Problem(name) {}
  void loadData(const std::string& fName, bool binary);
  void solve();
  void draw(Plotter& pl);
};

class ConvexHull : public Problem
{
  std::vector<vec> m_point;
  std::vector<int> m_vertexIdx;
public:
  ConvexHull(const std::string& name = "Find convex hull of points") : Problem(name) {}
  int n_data() { return 1; }
  void loadData(const std::vector<std::string>& fName, bool binary);
  void solve();
  void draw(Plotter& pl);
};

class KDTree;
class KDTreeBuild : public Problem
{
  std::vector<vec> m_point;
  dataset_type     *m_set;
  KDTree *m_tree;
public:
  KDTreeBuild(const std::string& name = "Build KDTree") : Problem(name), m_tree(NULL), m_set(NULL) {}
  int n_data() { return 1; }
  void loadData(const std::vector<std::string>& fName, bool binary);
  void solve();
  void draw(Plotter& pl);
  KDTree* tree() const { return m_tree; }
};

class Matching;
class MatchingProblem : public Problem
{
protected:
  std::vector<vec> m_point[2];
  std::vector<dataset_type*> m_set;
  Matching* m_match;
public:
  MatchingProblem(const std::string& name = "Matching") : Problem(name), m_match(NULL), m_set(2, NULL) {}
  int n_data() { return 2; }
  void loadData(const std::vector<std::string>& fName, bool binary);
  void solve();
  void draw(Plotter& pl);
  void createDatasets();
};

class AuctionMatchingProblem : public MatchingProblem
{
public:
  AuctionMatchingProblem(const std::string& name = "Auction Matching") : MatchingProblem(name) {}
  void solve();
};

class MaximumMatchingProblem : public MatchingProblem
{
public:
  double m_r;
public:
  MaximumMatchingProblem(const std::string& name = "Maximum Matching") : MatchingProblem(name), m_r(5.0) {}
  void solve();
};

class AuctionMatchingKDTreeProblem : public MatchingProblem
{
public:
  AuctionMatchingKDTreeProblem(const std::string& name = "Auction Matching with KDTree") : MatchingProblem(name) {}
  void solve();
};

namespace common {
  //void loadVVec(const std::string& fName, std::vector<vec>& vvec);
  void loadVVec(const std::string& fName, std::vector<vec>& vvec, bool binary);
  void drawVVec(const std::vector<vec>& vvec, Plotter& pl, const QPen& pen);
}

//
//class CoverTree;
//class CoverTreeBuild : public Problem
//{
//  std::vector<vec> m_point;
//  CoverTree *m_tree;
//public:
//  CoverTreeBuild(const std::string& name = "Build CoverTree") : Problem(name), m_tree(NULL) {}
//  void loadData(const std::string& fName);
//  void solve();
//  void draw(Plotter& pl);
//  CoverTree* tree() const { return m_tree; }
//};
//
//class Matching : public Problem
//{
//protected:
//  std::vector<vec> m_point1, m_point2;
//public:
//  Matching(const std::string& name = "Matching") : Problem(name) {}
//  void loadData(const std::string& fName) {}
//  virtual void loadData(const std::string& fName1, const std::string& fName2);
//  void solve();
//  void draw(Plotter& pl);
//
//  double weight(int i, int j) const { return arma::norm(m_point1[i]-m_point2[j], 2); }
//  double cost() const;
//protected:
//  std::vector<int> m_prev, m_match;
//  std::vector<double> m_p, m_dist;
//  int n;
//
//  int n1() const { return m_point1.size(); }
//  int n2() const { return m_point2.size(); }
//  double weight_p(int x, int y) const { return m_p[x]-m_p[y]+weight(x,y-n); }
//  int compute_shortest_distances();
//  void augment_path(int t);
//};
//
//class AuctionMatching : public Matching
//{
//public:
//  AuctionMatching(const std::string& name = "Auction matching") : Matching(name) {}
//  void solve();
//};
